// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// go:build ignore 
// +build ignore 

package main

// 通用操作码通常指定宽度。该运算的输入和输出
// 是给定的位宽。没有
// “符号”的概念，因此Add32可用于有符号和无符号32位
// 加法。

// Signed/unsigned是显式的，扩展名为ops 
// （SignExt*/ZeroExt*），对于某些操作码
// （例如，移位的第二个参数是unsigned）。如果没有提到，所有参数都接受有符号的输入，或者不关心它们的输入是有符号的还是无符号的。

var genericOps = []opData{
	// 2-输入算法
	// 类型必须与Go类型一致。例如，Add必须采用相同类型的两个值
	// 并生成相同的类型。
	{name: "Add8", argLength: 2, commutative: true}, // arg0+arg1 
	{name: "Add16", argLength: 2, commutative: true},
	{name: "Add32", argLength: 2, commutative: true},
	{name: "Add64", argLength: 2, commutative: true},
	{name: "AddPtr", argLength: 2}, // 用于地址计算。arg0是指针，arg1是整数。
	{name: "Add32F", argLength: 2, commutative: true},
	{name: "Add64F", argLength: 2, commutative: true},

	{name: "Sub8", argLength: 2}, // arg0-arg1 
	{name: "Sub16", argLength: 2},
	{name: "Sub32", argLength: 2},
	{name: "Sub64", argLength: 2},
	{name: "SubPtr", argLength: 2},
	{name: "Sub32F", argLength: 2},
	{name: "Sub64F", argLength: 2},

	{name: "Mul8", argLength: 2, commutative: true}, // arg0*arg1 
	{name: "Mul16", argLength: 2, commutative: true},
	{name: "Mul32", argLength: 2, commutative: true},
	{name: "Mul64", argLength: 2, commutative: true},
	{name: "Mul32F", argLength: 2, commutative: true},
	{name: "Mul64F", argLength: 2, commutative: true},

	{name: "Div32F", argLength: 2}, // arg0/arg1 
	{name: "Div64F", argLength: 2},

	{name: "Hmul32", argLength: 2, commutative: true},
	{name: "Hmul32u", argLength: 2, commutative: true},
	{name: "Hmul64", argLength: 2, commutative: true},
	{name: "Hmul64u", argLength: 2, commutative: true},

	{name: "Mul32uhilo", argLength: 2, typ: "(UInt32,UInt32)", commutative: true}, // arg0*arg1，返回（hi，lo）


	// 用于降低分界线强度的特殊说明。
	// 这些运算计算无符号（arg0+arg1）/2，将其更正为所有
	// 32/64位，即使加法的中间结果为33/65位。
	// 这些操作可以假定arg0>=arg1。
	// 注意：这些操作不是可交换的！
	{name: "Avg32u", argLength: 2, typ: "UInt32"}, // 32位平台仅
	{name: "Avg64u", argLength: 2, typ: "UInt64"}, // 64位平台仅

	// 对于Div16、Div32和Div64，AuxInt非零表示除数已被证明不是-1 
	// 或股息不是最负的值。
	{name: "Div8", argLength: 2},  // arg0/arg1，有符号
	{name: "Div8u", argLength: 2}, // arg0/arg1，无符号
	{name: "Div16", argLength: 2, aux: "Bool"},
	{name: "Div16u", argLength: 2},
	{name: "Div32", argLength: 2, aux: "Bool"},
	{name: "Div32u", argLength: 2},
	{name: "Div64", argLength: 2, aux: "Bool"},
	{name: "Div64u", argLength: 2},
	{name: "Div128u", argLength: 3}, // arg0:arg1/arg2（128位除以64位），返回（q，r）

	// 对于Mod16、Mod32和Mod64，AuxInt非零表示除数已被证明不是-1。
	{name: "Mod8", argLength: 2},  // arg0%arg1，有符号
	{name: "Mod8u", argLength: 2}, // arg0%arg1，无符号
	{name: "Mod16", argLength: 2, aux: "Bool"},
	{name: "Mod16u", argLength: 2},
	{name: "Mod32", argLength: 2, aux: "Bool"},
	{name: "Mod32u", argLength: 2},
	{name: "Mod64", argLength: 2, aux: "Bool"},
	{name: "Mod64u", argLength: 2},

	{name: "And8", argLength: 2, commutative: true}, // arg0和arg1 
	{name: "And16", argLength: 2, commutative: true},
	{name: "And32", argLength: 2, commutative: true},
	{name: "And64", argLength: 2, commutative: true},

	{name: "Or8", argLength: 2, commutative: true}, // arg0 | arg1 
	{name: "Or16", argLength: 2, commutative: true},
	{name: "Or32", argLength: 2, commutative: true},
	{name: "Or64", argLength: 2, commutative: true},

	{name: "Xor8", argLength: 2, commutative: true}, // arg0^arg1 
	{name: "Xor16", argLength: 2, commutative: true},
	{name: "Xor32", argLength: 2, commutative: true},
	{name: "Xor64", argLength: 2, commutative: true},

	// 对于移位，AxB表示移位值有A位，移位量有B位。
	// 移位金额被视为未签名。
	// 如果已知arg1为非负且小于arg0中的位数，则
	// 则生长素可设置为1。
	// 这可以在某些平台上更好地生成代码。
	{name: "Lsh8x8", argLength: 2, aux: "Bool"}, // arg0<<arg1 
	{name: "Lsh8x16", argLength: 2, aux: "Bool"},
	{name: "Lsh8x32", argLength: 2, aux: "Bool"},
	{name: "Lsh8x64", argLength: 2, aux: "Bool"},
	{name: "Lsh16x8", argLength: 2, aux: "Bool"},
	{name: "Lsh16x16", argLength: 2, aux: "Bool"},
	{name: "Lsh16x32", argLength: 2, aux: "Bool"},
	{name: "Lsh16x64", argLength: 2, aux: "Bool"},
	{name: "Lsh32x8", argLength: 2, aux: "Bool"},
	{name: "Lsh32x16", argLength: 2, aux: "Bool"},
	{name: "Lsh32x32", argLength: 2, aux: "Bool"},
	{name: "Lsh32x64", argLength: 2, aux: "Bool"},
	{name: "Lsh64x8", argLength: 2, aux: "Bool"},
	{name: "Lsh64x16", argLength: 2, aux: "Bool"},
	{name: "Lsh64x32", argLength: 2, aux: "Bool"},
	{name: "Lsh64x64", argLength: 2, aux: "Bool"},

	{name: "Rsh8x8", argLength: 2, aux: "Bool"}, // arg0>>arg1，有符号
	{name: "Rsh8x16", argLength: 2, aux: "Bool"},
	{name: "Rsh8x32", argLength: 2, aux: "Bool"},
	{name: "Rsh8x64", argLength: 2, aux: "Bool"},
	{name: "Rsh16x8", argLength: 2, aux: "Bool"},
	{name: "Rsh16x16", argLength: 2, aux: "Bool"},
	{name: "Rsh16x32", argLength: 2, aux: "Bool"},
	{name: "Rsh16x64", argLength: 2, aux: "Bool"},
	{name: "Rsh32x8", argLength: 2, aux: "Bool"},
	{name: "Rsh32x16", argLength: 2, aux: "Bool"},
	{name: "Rsh32x32", argLength: 2, aux: "Bool"},
	{name: "Rsh32x64", argLength: 2, aux: "Bool"},
	{name: "Rsh64x8", argLength: 2, aux: "Bool"},
	{name: "Rsh64x16", argLength: 2, aux: "Bool"},
	{name: "Rsh64x32", argLength: 2, aux: "Bool"},
	{name: "Rsh64x64", argLength: 2, aux: "Bool"},

	{name: "Rsh8Ux8", argLength: 2, aux: "Bool"}, // arg0>>arg1，无符号
	{name: "Rsh8Ux16", argLength: 2, aux: "Bool"},
	{name: "Rsh8Ux32", argLength: 2, aux: "Bool"},
	{name: "Rsh8Ux64", argLength: 2, aux: "Bool"},
	{name: "Rsh16Ux8", argLength: 2, aux: "Bool"},
	{name: "Rsh16Ux16", argLength: 2, aux: "Bool"},
	{name: "Rsh16Ux32", argLength: 2, aux: "Bool"},
	{name: "Rsh16Ux64", argLength: 2, aux: "Bool"},
	{name: "Rsh32Ux8", argLength: 2, aux: "Bool"},
	{name: "Rsh32Ux16", argLength: 2, aux: "Bool"},
	{name: "Rsh32Ux32", argLength: 2, aux: "Bool"},
	{name: "Rsh32Ux64", argLength: 2, aux: "Bool"},
	{name: "Rsh64Ux8", argLength: 2, aux: "Bool"},
	{name: "Rsh64Ux16", argLength: 2, aux: "Bool"},
	{name: "Rsh64Ux32", argLength: 2, aux: "Bool"},
	{name: "Rsh64Ux64", argLength: 2, aux: "Bool"},

	// 2-输入比较
	{name: "Eq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0==arg1 
	{name: "Eq16", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Eq32", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Eq64", argLength: 2, commutative: true, typ: "Bool"},
	{name: "EqPtr", argLength: 2, commutative: true, typ: "Bool"},
	{name: "EqInter", argLength: 2, typ: "Bool"}, // arg0或arg1为零；前端
	{name: "EqSlice", argLength: 2, typ: "Bool"}, // arg0或arg1处理的其他案件为零；前端
	{name: "Eq32F", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Eq64F", argLength: 2, commutative: true, typ: "Bool"},

	{name: "Neq8", argLength: 2, commutative: true, typ: "Bool"}, // arg0！=arg1 
	{name: "Neq16", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Neq32", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Neq64", argLength: 2, commutative: true, typ: "Bool"},
	{name: "NeqPtr", argLength: 2, commutative: true, typ: "Bool"},
	{name: "NeqInter", argLength: 2, typ: "Bool"}, // arg0或arg1为零；前端
	{name: "NeqSlice", argLength: 2, typ: "Bool"}, // arg0或arg1处理的其他案件为零；前端
	{name: "Neq32F", argLength: 2, commutative: true, typ: "Bool"},
	{name: "Neq64F", argLength: 2, commutative: true, typ: "Bool"},

	{name: "Less8", argLength: 2, typ: "Bool"},  // arg0<arg1，签名
	{name: "Less8U", argLength: 2, typ: "Bool"}, // arg0<arg1，未签名
	{name: "Less16", argLength: 2, typ: "Bool"},
	{name: "Less16U", argLength: 2, typ: "Bool"},
	{name: "Less32", argLength: 2, typ: "Bool"},
	{name: "Less32U", argLength: 2, typ: "Bool"},
	{name: "Less64", argLength: 2, typ: "Bool"},
	{name: "Less64U", argLength: 2, typ: "Bool"},
	{name: "Less32F", argLength: 2, typ: "Bool"},
	{name: "Less64F", argLength: 2, typ: "Bool"},

	{name: "Leq8", argLength: 2, typ: "Bool"},  // arg0<=arg1，签名
	{name: "Leq8U", argLength: 2, typ: "Bool"}, // arg0<=arg1，未签名
	{name: "Leq16", argLength: 2, typ: "Bool"},
	{name: "Leq16U", argLength: 2, typ: "Bool"},
	{name: "Leq32", argLength: 2, typ: "Bool"},
	{name: "Leq32U", argLength: 2, typ: "Bool"},
	{name: "Leq64", argLength: 2, typ: "Bool"},
	{name: "Leq64U", argLength: 2, typ: "Bool"},
	{name: "Leq32F", argLength: 2, typ: "Bool"},
	{name: "Leq64F", argLength: 2, typ: "Bool"},

	// CondSelect的类型与其第一个
	// 两个参数的类型相同，应该是寄存器宽度标量；第三个
	// 参数应该是布尔值
	{name: "CondSelect", argLength: 3}, // arg2？arg0:arg1 

	// 布尔运算
	{name: "AndB", argLength: 2, commutative: true, typ: "Bool"}, // arg0&&arg1（未短路）
	{name: "OrB", argLength: 2, commutative: true, typ: "Bool"},  // arg0 | | arg1（未短路）
	{name: "EqB", argLength: 2, commutative: true, typ: "Bool"},  // arg0==arg1 
	{name: "NeqB", argLength: 2, commutative: true, typ: "Bool"}, // arg0！=arg1 
	{name: "Not", argLength: 1, typ: "Bool"},                     // /！arg0，布尔运算

	// 1-input ops 
	{name: "Neg8", argLength: 1}, // -arg0 
	{name: "Neg16", argLength: 1},
	{name: "Neg32", argLength: 1},
	{name: "Neg64", argLength: 1},
	{name: "Neg32F", argLength: 1},
	{name: "Neg64F", argLength: 1},

	{name: "Com8", argLength: 1}, // ^arg0 
	{name: "Com16", argLength: 1},
	{name: "Com32", argLength: 1},
	{name: "Com64", argLength: 1},

	{name: "Ctz8", argLength: 1},         // 计数尾随（低阶）零（返回0-8）
	{name: "Ctz16", argLength: 1},        // 计数尾随（低阶）零（返回0-16）
	{name: "Ctz32", argLength: 1},        // 计数尾随（低阶）零（返回0-32）
	{name: "Ctz16NonZero", argLength: 1}, // 同上，但arg[0]已知为非零，返回0-15 
	{name: "Ctz32NonZero", argLength: 1}, // 同上，但arg[0]已知为非零，返回0-31 
	{name: "Ctz64NonZero", argLength: 1}, // 同上，但arg[0]已知为非零，返回0-63 
	{name: "BitLen8", argLength: 1},      // arg[0]中的位数（返回0-8）
	{name: "BitLen16", argLength: 1},     // arg[0]中的位数（返回0-16）
	{name: "BitLen32", argLength: 1},     // arg[0]中的位数（返回0-32）
	{name: "BitLen64", argLength: 1},     // arg[0]中的位数（返回0-64）

	{name: "Bswap32", argLength: 1}, // 交换字节
	{name: "Bswap64", argLength: 1}, // 交换字节

	{name: "BitRev32", argLength: 1}, // 反转arg[0]
	{name: "BitRev64", argLength: 1}, // 反转arg[0]

	{name: "PopCount8", argLength: 1},    // 计数arg[0]
	{name: "PopCount16", argLength: 1},   // 计数arg[0]
	{name: "PopCount32", argLength: 1},   // 计数arg[0]
	{name: "PopCount64", argLength: 1},   // 计数arg[0]
	{name: "RotateLeft8", argLength: 2},  // 旋转arg[0]中arg[1]
	{name: "RotateLeft16", argLength: 2}, // 旋转arg0]arg[1]
	{name: "RotateLeft32", argLength: 2}, // 旋转arg[0]中的位arg[1]
	{name: "RotateLeft64", argLength: 2}, // 旋转arg[0]中的位arg[1]

	// 平方根。
	// 特殊情况：
	// +∞  → +∞ 
	// /±0→ ±0（保留符号）
	// x<0→ NaN 
	// NaN→ NaN 
	{name: "Sqrt", argLength: 1},   // √arg0（浮点，双精度）
	{name: "Sqrt32", argLength: 1}, // √arg0（浮点，单精度）

	// 四舍五入为整数，仅浮点64。
	// 特殊情况：
	// ”∞  → ±∞ （保留符号）
	// /±0→ ±0（保留符号）
	// NaN→ NaN 
	{name: "Floor", argLength: 1},       // 将arg0绕向-∞ 
	{name: "Ceil", argLength: 1},        // 将arg0绕向+∞ 
	{name: "Trunc", argLength: 1},       // 将arg0取整为0 
	{name: "Round", argLength: 1},       // 将arg0取整为最近，将arg0取整为最近，将arg0取整为最近，将arg0取整为偶数

	// 修改符号位
	{name: "Abs", argLength: 1},      // 绝对值arg0 
	{name: "Copysign", argLength: 2}, // 将符号从arg0复制到arg1 

	// 3-输入操作码。
	// 融合乘加，仅浮点64。
	// 如果a*b+c正好为零（四舍五入之前），则结果为+0或-0。
	// 0的符号根据
	// 加法的标准规则确定（-0，如果a*b和c都是-0，否则+0）。
	// 
	// 否则，当a*b+c四舍五入为零时，结果0的符号为
	// 由精确结果a*b+c的符号确定。
	// 参见ieee754第6.3节。
	// 
	// 当乘法为无穷乘以零时，结果为NaN。
	// 参见ieee754第7.2节。
	{name: "FMA", argLength: 3}, // 不带中间舍入的计算（a*b）+c 

	// 数据移动。Phi的最大参数长度是不确定的。
	{name: "Phi", argLength: -1, zeroWidth: true}, // 选择一个参数，该参数基于我们从
	{name: "Copy", argLength: 1},                  // 输出=arg0 
	// 在指针和整数之间进行转换。
	// 为了避免混淆GC 
	// （尤其是堆栈映射），我们为此提供了一个特殊的操作。它需要一个内存arg，因此它
	// 可以根据GC安全点正确排序。
	// 它被编译为空，因此它的结果必须与它的参数注册在同一个
	// 寄存器中。regalloc知道它可以使用任何
	// 可分配整数寄存器进行OpConvert。
	// arg0=ptr/int arg1=mem，output=int/ptr 
	{name: "Convert", argLength: 2, zeroWidth: true, resultInArg0: true},

	// 常量。常量值存储在aux或
	// auxint字段中。
	{name: "ConstBool", aux: "Bool"},     // auxint为0表示假，1表示真
	{name: "ConstString", aux: "String"}, // 值为aux。（字符串）
	{name: "ConstNil", typ: "BytePtr"},   // 零指针
	{name: "Const8", aux: "Int8"},        // auxint是符号扩展的8位
	{name: "Const16", aux: "Int16"},      // auxint是符号扩展的16位
	{name: "Const32", aux: "Int32"},      // auxint是符号扩展的32位
	// 注意：即使值的类型是无符号的，ConstX也是符号扩展的。
	// 例如，uint8（0xaa）存储为auxint=0xffffffffffa。
	{name: "Const64", aux: "Int64"}, // 值为生长素
	// 注意：对于Const32F和Const64F，我们都不允许编码NaN。
	// 发出信号是很棘手的，因为如果你对它们做任何事情，它们就会变得安静。
	// 特别是，将32位sNaN转换为64位，然后再将其转换为qNaN。
	// 见第36399和36400期。
	// 可以对+inf、-inf和-0进行编码。
	{name: "Const32F", aux: "Float32"}, // 值就是数学。Float64frombits（uint64（auxint））和float 32 
	{name: "Const64F", aux: "Float64"}, // 值是math。Float64frombits（uint64（auxint））
	{name: "ConstInterface"},           // nil接口
	{name: "ConstSlice"},               // nil slice 

	// Constant like things 
	{name: "InitMem", zeroWidth: true},                               // 函数的内存输入。
	{name: "Arg", aux: "SymOff", symEffect: "Read", zeroWidth: true}, // 函数的参数。aux=参数的GCNode，off=该参数中的偏移量。

	// 和Arg一样，这些都是通用的操作系统，可以在下降过程中存活下来。AuxInt是一个寄存器索引，每个索引的实际输出寄存器由体系结构定义。
	// AuxInt=整数参数索引（不是寄存器号）。ABI指定了从函数
	{name: "ArgIntReg", aux: "NameOffsetInt8", zeroWidth: true},   // 中获取的指向int reg中函数的参数。
	{name: "ArgFloatReg", aux: "NameOffsetInt8", zeroWidth: true}, // 浮点寄存器中函数的参数。

	// 变量的地址。arg0是基指针。
	// 如果变量是全局变量，则基指针将是SB，
	// 辅助字段将是*obj。伊西姆。
	// 如果变量是局部变量，则基指针将是SP，
	// 辅助字段将是*gc。节点。
	{name: "Addr", argLength: 1, aux: "Sym", symEffect: "Addr"},      // 变量的地址。Arg0=SB。Aux标识变量。
	{name: "LocalAddr", argLength: 2, aux: "Sym", symEffect: "Addr"}, // 变量的地址。Arg0=SP.Arg1=mem。Aux标识变量。

	{name: "SP", zeroWidth: true},                 // 堆栈指针
	{name: "SB", typ: "Uintptr", zeroWidth: true}, // 静态基指针（又称全局指针）
	{name: "Invalid"},                             // 未使用的值

	// 内存操作
	{name: "Load", argLength: 2},                          // 从arg0加载。arg1=内存
	{name: "Dereference", argLength: 2},                   // 从arg0加载。arg1=内存。对于arg/result传递，result是一个不支持SSA的“值”。
	{name: "Store", argLength: 3, typ: "Mem", aux: "Typ"}, // 存储arg1到arg0。arg2=内存，aux=类型。返回内存。
	// 在某些情况下，移动的源和目标可能会重叠。参见例如
	// memmove内联。规则。当INLINEABLEMMOVESIZE（in../rewrite.go）
	// 返回true时，我们必须在所有存储之前执行所有加载，降低移动。
	// 移动类型用于写入屏障过程以插入写入屏障
	// 并用于在某些架构上对齐。
	// 对于无指针类型，类型可能不准确。
	// 对于类型对齐和指针信息，请使用Aux中的类型；
	// 对于类型大小，使用生长素中的大小。
	// 重写规则“inline runtime.memmove”生成的移动类型不准确，
	// 例如字节类型，而不是更准确的字节类型[8]。
	{name: "Move", argLength: 3, typ: "Mem", aux: "TypSize"}, // arg0=destptr，arg1=srcptr，arg2=mem，auxint=size，aux=type。返回内存。
	{name: "Zero", argLength: 2, typ: "Mem", aux: "TypSize"}, // arg0=destptr，arg1=mem，auxint=size，aux=type。返回内存。

	// 带写屏障的内存操作。
	// 扩展到运行时调用。如果在堆栈上写入，写屏障将被移除。
	{name: "StoreWB", argLength: 3, typ: "Mem", aux: "Typ"},    // 将arg1存储到arg0。arg2=内存，aux=类型。返回内存。
	{name: "MoveWB", argLength: 3, typ: "Mem", aux: "TypSize"}, // arg0=destptr，arg1=srcptr，arg2=mem，auxint=size，aux=type。返回内存。
	{name: "ZeroWB", argLength: 2, typ: "Mem", aux: "TypSize"}, // arg0=destptr，arg1=mem，auxint=size，aux=type。返回内存。

	// WB调用运行时。gcWriteBarrier。这不是一个正常的
	// 调用：它在寄存器中接受参数，不会阻塞
	// 通用寄存器（确切的阻塞器集是
	// 依赖于arch），并且不是一个安全点。
	{name: "WB", argLength: 3, typ: "Mem", aux: "Sym", symEffect: "None"}, // arg0=destptr，arg1=srcptr，arg2=mem，aux=runtime。gcWriteBarrier 

	{name: "HasCPUFeature", argLength: 0, typ: "bool", aux: "Sym", symEffect: "None"}, // aux=可以从

	// 加载此功能标志的位置PanicBounds和PanicExtend生成运行时死机。
	// 它们的参数提供了在紧急消息中使用的索引值。
	// PanicBounds和PanicExtend都有BoundsKind类型中的生长素值（in../op.go）。
	// PanicBounds的索引大小为整数。
	// PanicExtend的索引大小为int64。（PanicExtend仅用于32位ARCH。）
	{name: "PanicBounds", argLength: 3, aux: "Int64", typ: "Mem", call: true}, // arg0=idx，arg1=len，arg2=mem，返回内存。
	{name: "PanicExtend", argLength: 4, aux: "Int64", typ: "Mem", call: true}, // arg0=idxHi，arg1=idxLo，arg2=len，arg3=mem，返回内存。

	// 函数调用。调用的参数已写入堆栈。
	// 返回值出现在堆栈上。方法接收方（如果有）被视为
	// 幻象第一个参数。
	// TODO（josharian）：ClosureCall和InterCall应该有Int32 aux 
	// 以匹配StaticCall的32位参数大小限制。
	// TODO（drchase，josharian）：arg大小限制是否可以捆绑到取消规则中？在中间值下降之前，LeCales接收固定输入（第一）、内存（最后）、ABCFDG 

	// 它们产生数量可变的结果值。
	// 这些值不一定是“SSA-able”；它们可能太大，
	// 但在这种情况下，输入会在OpDereference之前立即加载，而输出会立即用OpStore存储。
	// 
	// 调用扩展后，调用具有相同的固定中间内存输入排列，
	// 不同的是，“中间”只是寄存器驻留的输入，
	// 和非寄存器输入存储在从SP 
	// 开始的ABI定义的偏移量处（存储线程穿过最终作为调用输入的内存）。
	// 输出遵循类似的模式；寄存器驻留输出是结果类型输出的前导元素
	// 最后是内存，任何内存驻留输出都已被
	// 存储到ABI定义的位置。每个非内存输入或输出适合一个寄存器。
	// 
	// 后续特定于体系结构的降低只会更改操作码。

	{name: "ClosureCall", argLength: -1, aux: "CallOff", call: true}, // arg0=代码指针，arg1=上下文ptr，arg2。。argN-1是寄存器输入，argN=内存。生长素=arg大小。返回寄存器结果加内存的结果。
	{name: "StaticCall", argLength: -1, aux: "CallOff", call: true},  // 调用函数aux。（*obj.LSym），arg0。。argN-1是寄存器输入，argN=内存。生长素=arg大小。返回寄存器结果加内存的结果。
	{name: "InterCall", argLength: -1, aux: "CallOff", call: true},   // 接口调用。arg0=代码指针arg1。。argN-1是寄存器输入，argN=memory，auxint=arg size。返回寄存器结果加内存的结果。
	{name: "TailCall", argLength: -1, aux: "CallOff", call: true},    // 尾部调用函数aux。（*obj.LSym），arg0。。argN-1是寄存器输入，argN=内存。生长素=arg大小。返回寄存器结果加内存的结果。

	{name: "ClosureLECall", argLength: -1, aux: "CallOff", call: true}, // 延迟扩展关闭调用。arg0=代码指针，arg1=上下文ptr，arg2。。argN-1是输入，argN是mem。生长素=arg大小。结果是结果的元组加上mem。
	{name: "StaticLECall", argLength: -1, aux: "CallOff", call: true},  // 后期扩展的静态调用函数aux。（*ssa.AuxCall.Fn）。arg0。。argN-1是输入，argN是mem。生长素=arg大小。结果是结果的元组加上mem。
	{name: "InterLECall", argLength: -1, aux: "CallOff", call: true},   // 延迟扩展接口调用。arg0=代码指针arg1。。argN-1是输入，argN是mem。生长素=arg大小。结果是结果的元组加上mem。
	{name: "TailLECall", argLength: -1, aux: "CallOff", call: true},    // 后期扩展的静态尾部调用函数aux。（*ssa.AuxCall.Fn）。arg0。。argN-1是输入，argN是mem。生长素=arg大小。结果是结果的元组加上mem。

	// 转换：有符号扩展、零（无符号）扩展、截断
	{name: "SignExt8to16", argLength: 1, typ: "Int16"},
	{name: "SignExt8to32", argLength: 1, typ: "Int32"},
	{name: "SignExt8to64", argLength: 1, typ: "Int64"},
	{name: "SignExt16to32", argLength: 1, typ: "Int32"},
	{name: "SignExt16to64", argLength: 1, typ: "Int64"},
	{name: "SignExt32to64", argLength: 1, typ: "Int64"},
	{name: "ZeroExt8to16", argLength: 1, typ: "UInt16"},
	{name: "ZeroExt8to32", argLength: 1, typ: "UInt32"},
	{name: "ZeroExt8to64", argLength: 1, typ: "UInt64"},
	{name: "ZeroExt16to32", argLength: 1, typ: "UInt32"},
	{name: "ZeroExt16to64", argLength: 1, typ: "UInt64"},
	{name: "ZeroExt32to64", argLength: 1, typ: "UInt64"},
	{name: "Trunc16to8", argLength: 1},
	{name: "Trunc32to8", argLength: 1},
	{name: "Trunc32to16", argLength: 1},
	{name: "Trunc64to8", argLength: 1},
	{name: "Trunc64to16", argLength: 1},
	{name: "Trunc64to32", argLength: 1},

	{name: "Cvt32to32F", argLength: 1},
	{name: "Cvt32to64F", argLength: 1},
	{name: "Cvt64to32F", argLength: 1},
	{name: "Cvt64to64F", argLength: 1},
	{name: "Cvt32Fto32", argLength: 1},
	{name: "Cvt32Fto64", argLength: 1},
	{name: "Cvt64Fto32", argLength: 1},
	{name: "Cvt64Fto64", argLength: 1},
	{name: "Cvt32Fto64F", argLength: 1},
	{name: "Cvt64Fto32F", argLength: 1},
	{name: "CvtBoolToUint8", argLength: 1},

	// 强制四舍五入到类型精度。
	{name: "Round32F", argLength: 1},
	{name: "Round64F", argLength: 1},

	// 自动插入的安全检查
	{name: "IsNonNil", argLength: 1, typ: "Bool"},        // arg0！=无
	{name: "IsInBounds", argLength: 2, typ: "Bool"},      // 0<=arg0<arg1。arg1保证大于等于0。
	{name: "IsSliceInBounds", argLength: 2, typ: "Bool"}, // 0<=arg0<=arg1。arg1保证大于等于0。
	{name: "NilCheck", argLength: 2, typ: "Void"},        // arg0=ptr，arg1=mem。如果arg0为零，则恐慌。返回无效。

	// 伪操作
	{name: "GetG", argLength: 1, zeroWidth: true}, // 运行时。getg（）（读取g指针）。arg0=mem-
	{name: "GetClosurePtr"},                       // 从专用寄存器获取闭包指针
	{name: "GetCallerPC"},                         // 对于GetCallerRPC内部
	{name: "GetCallerSP"},                         // 对于getcallersp内部

	// 索引操作
	{name: "PtrIndex", argLength: 2},             // arg0=ptr，arg1=index。计算ptr+sizeof（*v.type）*索引，其中索引扩展为ptrwidth类型
	{name: "OffPtr", argLength: 1, aux: "Int64"}, // arg0+生长素（arg0和结果是指针）

	// Slices 
	{name: "SliceMake", argLength: 3},                // arg0=ptr，arg1=len，arg2=cap 
	{name: "SlicePtr", argLength: 1, typ: "BytePtr"}, // ptr（arg0）
	{name: "SliceLen", argLength: 1},                 // len len 
	{name: "SliceCap", argLength: 1},                 // cap 
	// SlicePtr值被假定为非零，因为它们受到边界检查的保护。
	// SlicePtrUnchecked值可以为零。
	{name: "SlicePtrUnchecked", argLength: 1},

	// 复杂（部分/整体）
	{name: "ComplexMake", argLength: 2}, // arg0=real，arg1=imag 
	{name: "ComplexReal", argLength: 1}, // real（arg0）
	{name: "ComplexImag", argLength: 1}, // imag（arg0）

	// 字符串
	{name: "StringMake", argLength: 2},                // arg0=ptr，arg1=len 

	{name: "IData", argLength: 1},                // arg0=接口，返回数据字段

	// Structs 
	{name: "StructMake0"},                              // 返回带0个字段的结构。
	{name: "StructMake1", argLength: 1},                // arg0=field0。返回struct。
	{name: "StructMake2", argLength: 2},                // arg0，arg1=field0，field1。返回struct。
	{name: "StructMake3", argLength: 3},                // arg0。。2=字段0。。2.返回struct。
	{name: "StructMake4", argLength: 4},                // arg0。。3=字段0。。3.返回struct。
	{name: "StructSelect", argLength: 1, aux: "Int64"}, // arg0=struct，auxint=field index。返回生长素字段。

	// 数组
	{name: "ArrayMake0"},                              // 返回包含0个元素的数组
	{name: "ArrayMake1", argLength: 1},                // 返回包含1个元素的数组
	{name: "ArraySelect", argLength: 1, aux: "Int64"}, // arg0=array，auxint=index。返回一个[i]。

	// 寄存器分配器的溢出和恢复操作。这些是
	// 在语义上与OpCopy相同；他们不像常规内存操作那样接收/返回
	// 存储。我们可以不使用内存
	// args，因为我们知道堆栈上没有溢出槽的别名。
	{name: "StoreReg", argLength: 1},
	{name: "LoadReg", argLength: 1},

	// 在ssa施工期间使用。与Copy类似，但尚未指定arg。
	{name: "FwdRef", aux: "Sym", symEffect: "None"},

	// 未知值。用于值不重要的值，因为它们是死代码。
	{name: "Unknown"},

	{name: "VarDef", argLength: 1, aux: "Sym", typ: "Mem", symEffect: "None", zeroWidth: true}, // aux是一个*gc。即将初始化的变量的节点。arg0=mem，返回mem 
	{name: "VarKill", argLength: 1, aux: "Sym", symEffect: "None"},                             // aux是一个*gc。已知为死的变量的节点。arg0=mem，返回mem 
	// TODO:VarLive和KeepAlive有什么区别？
	{name: "VarLive", argLength: 1, aux: "Sym", symEffect: "Read", zeroWidth: true}, // aux是一个*gc。必须保持活动状态的变量的节点。arg0=mem，返回mem 
	{name: "KeepAlive", argLength: 2, typ: "Mem", zeroWidth: true},                  // arg[0]是一个在该标记之前必须保持活动状态的值。arg[1]=mem，返回mem 

	// InlMark标记内联函数体的开始。它的生长素字段
	// 区分它标记的本地内联树中的哪个条目。
	{name: "InlMark", argLength: 1, aux: "Int32", typ: "Void"}, // arg[0]=mem，返回void。

	// 32位体系结构上中断64位操作的操作
	{name: "Int64Make", argLength: 2, typ: "UInt64"}, // arg0=hi，arg1=lo 
	{name: "Int64Hi", argLength: 1, typ: "UInt32"},   // arg0的高32位
	{name: "Int64Lo", argLength: 1, typ: "UInt32"},   // arg0的低32位

	{name: "Add32carry", argLength: 2, commutative: true, typ: "(UInt32,Flags)"}, // arg0+arg1，返回（值，进位）

	{name: "Sub32withcarry", argLength: 3},                    // arg0-arg1-arg2，arg2=进位（0或1）

	{name: "Add64carry", argLength: 3, commutative: true, typ: "(UInt64,UInt64)"}, // arg0+arg1+arg2，arg2必须为0或1。返回（value，value>>64）
	{name: "Sub64borrow", argLength: 3, typ: "(UInt64,UInt64)"},                   // arg0-（arg1+arg2），arg2必须是0或1。返回（value，value>>64&1）

	{name: "Signmask", argLength: 1, typ: "Int32"},  // 0如果arg0>=0，-1如果arg0<0 
	{name: "Zeromask", argLength: 1, typ: "UInt32"}, // 0如果arg0==0，0xffffff如果arg0！=0 
	{name: "Slicemask", argLength: 1},               // 0如果arg0==0，则为-1如果arg0>0，如果arg0<0，则为未定义。类型是本机整数大小。

	{name: "SpectreIndex", argLength: 2},      // 如果0<=arg0<arg1，则为arg0，否则为0。类型是本机整数大小。
	{name: "SpectreSliceIndex", argLength: 2}, // arg0如果0<=arg0<=arg1，则为0。类型是本机整数大小。

	{name: "Cvt32Uto32F", argLength: 1}, // uint32->float32，仅用于32位arch 
	{name: "Cvt32Uto64F", argLength: 1}, // uint32->float64，仅用于32位arch 
	{name: "Cvt32Fto32U", argLength: 1}, // float32->uint32，仅用于32位arch 
	{name: "Cvt32Fto64U", argLength: 1}, // float32->uint64的ARCH，仅用于指令
	{name: "Cvt64Fto64U", argLength: 1}, // float64->uint64的ARCH，仅用于指令为

	// 用于断开元组的伪操作
	{name: "Select0", argLength: 1, zeroWidth: true},  // 元组的第一个组件
	{name: "Select1", argLength: 1, zeroWidth: true},  // 元组的第二个组件
	{name: "SelectN", argLength: 1, aux: "Int64"},     // arg0=result，auxint=field index的ARCH。返回生长素成员。
	{name: "SelectNAddr", argLength: 1, aux: "Int64"}, // arg0=result，auxint=field index。返回生长素成员的地址。用于不支持SSA的结果类型。
	{name: "MakeResult", argLength: -1},               // arg0。。是“结果”的组成部分（比如调用的结果）。最后一个参数应该是内存（比如调用的结果）。

	// 用于语义内联sync/Atomic和
	// 运行时/内部/原子的原子操作。原子加载返回一个新的内存，以便
	// 这些加载相对于其他加载和
	// 存储正确排序。
	{name: "AtomicLoad8", argLength: 2, typ: "(UInt8,Mem)"},                                    // 从arg0加载。arg1=内存。返回加载的值和新内存。
	{name: "AtomicLoad32", argLength: 2, typ: "(UInt32,Mem)"},                                  // 从arg0加载。arg1=内存。返回加载的值和新内存。
	{name: "AtomicLoad64", argLength: 2, typ: "(UInt64,Mem)"},                                  // 从arg0加载。arg1=内存。返回加载的值和新内存。
	{name: "AtomicLoadPtr", argLength: 2, typ: "(BytePtr,Mem)"},                                // 从arg0加载。arg1=内存。返回加载的值和新内存。
	{name: "AtomicLoadAcq32", argLength: 2, typ: "(UInt32,Mem)"},                               // 从arg0加载。arg1=内存。锁定采集，返回加载值和新内存。
	{name: "AtomicLoadAcq64", argLength: 2, typ: "(UInt64,Mem)"},                               // 从arg0加载。arg1=内存。锁定采集，返回加载值和新内存。
	{name: "AtomicStore8", argLength: 3, typ: "Mem", hasSideEffects: true},                     // 将arg1存储到*arg0。arg2=内存。返回内存。
	{name: "AtomicStore32", argLength: 3, typ: "Mem", hasSideEffects: true},                    // 将arg1存储到*arg0。arg2=内存。返回内存。
	{name: "AtomicStore64", argLength: 3, typ: "Mem", hasSideEffects: true},                    // 将arg1存储到*arg0。arg2=内存。返回内存。
	{name: "AtomicStorePtrNoWB", argLength: 3, typ: "Mem", hasSideEffects: true},               // 将arg1存储到*arg0。arg2=内存。返回内存。
	{name: "AtomicStoreRel32", argLength: 3, typ: "Mem", hasSideEffects: true},                 // 将arg1存储到*arg0。arg2=内存。锁定释放，返回内存。
	{name: "AtomicStoreRel64", argLength: 3, typ: "Mem", hasSideEffects: true},                 // 将arg1存储到*arg0。arg2=内存。释放锁，返回内存。
	{name: "AtomicExchange32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true},        // 将arg1存储到*arg0。arg2=内存。返回*arg0的旧内容和新内存。
	{name: "AtomicExchange64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true},        // 将arg1存储到*arg0。arg2=内存。返回*arg0的旧内容和新内存。
	{name: "AtomicAdd32", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true},             // Do*arg0+=arg1。arg2=内存。返回总和和新内存。
	{name: "AtomicAdd64", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true},             // Do*arg0+=arg1。arg2=内存。返回总和和新内存。
	{name: "AtomicCompareAndSwap32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true},    // 如果*arg0==arg1，则设置*arg0=arg2。如果发生存储并创建新内存，则返回true。
	{name: "AtomicCompareAndSwap64", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true},    // 如果*arg0==arg1，则设置*arg0=arg2。如果发生存储并创建新内存，则返回true。
	{name: "AtomicCompareAndSwapRel32", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // 如果*arg0==arg1，则设置*arg0=arg2。锁释放，报告是否发生存储和新内存。
	{name: "AtomicAnd8", argLength: 3, typ: "Mem", hasSideEffects: true},                       // /*arg0&=arg1。arg2=内存。返回内存。
	{name: "AtomicAnd32", argLength: 3, typ: "Mem", hasSideEffects: true},                      // /*arg0&=arg1。arg2=内存。返回内存。
	{name: "AtomicOr8", argLength: 3, typ: "Mem", hasSideEffects: true},                        // /*arg0 |=arg1。arg2=内存。返回内存。
	{name: "AtomicOr32", argLength: 3, typ: "Mem", hasSideEffects: true},                       // /*arg0 |=arg1。arg2=内存。返回内存。

	// 原子操作变体
	// 这些变体的语义与上述原子操作相同。
	// 但它们用于在某些现代机器上生成更高效的代码，并带有运行时CPU功能检测。
	// 目前，它们仅用于ARM64。
	{name: "AtomicAdd32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true},          // Do*arg0+=arg1。arg2=内存。返回总和和新内存。
	{name: "AtomicAdd64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true},          // Do*arg0+=arg1。arg2=内存。返回总和和新内存。
	{name: "AtomicExchange32Variant", argLength: 3, typ: "(UInt32,Mem)", hasSideEffects: true},     // 将arg1存储到*arg0。arg2=内存。返回*arg0的旧内容和新内存。
	{name: "AtomicExchange64Variant", argLength: 3, typ: "(UInt64,Mem)", hasSideEffects: true},     // 将arg1存储到*arg0。arg2=内存。返回*arg0的旧内容和新内存。
	{name: "AtomicCompareAndSwap32Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // 如果*arg0==arg1，则设置*arg0=arg2。如果发生存储并创建新内存，则返回true。
	{name: "AtomicCompareAndSwap64Variant", argLength: 4, typ: "(Bool,Mem)", hasSideEffects: true}, // 如果*arg0==arg1，则设置*arg0=arg2。如果发生存储并创建新内存，则返回true。
	{name: "AtomicAnd8Variant", argLength: 3, typ: "Mem", hasSideEffects: true},                    // /*arg0&=arg1。arg2=内存。返回内存。
	{name: "AtomicAnd32Variant", argLength: 3, typ: "Mem", hasSideEffects: true},                   // /*arg0&=arg1。arg2=内存。返回内存。
	{name: "AtomicOr8Variant", argLength: 3, typ: "Mem", hasSideEffects: true},                     // /*arg0 |=arg1。arg2=内存。返回内存。
	{name: "AtomicOr32Variant", argLength: 3, typ: "Mem", hasSideEffects: true},                    // /*arg0 |=arg1。arg2=内存。返回内存。

	// 发布障碍
	{name: "PubBarrier", argLength: 1, hasSideEffects: true}, // Do数据障碍。arg0=内存。

	// Clobber实验op 
	{name: "Clobber", argLength: 0, typ: "Void", aux: "SymOff", symEffect: "None"}, // 将无效指针值写入堆栈变量
	{name: "ClobberReg", argLength: 0, typ: "Void"},                                // Clobber寄存器

	// 预取指令
	{name: "PrefetchCache", argLength: 2, hasSideEffects: true},         // 将arg0预取到缓存。arg0=addr，arg1=memory。
	{name: "PrefetchCacheStreamed", argLength: 2, hasSideEffects: true}, // 对缓存进行非时态或流式预取arg0。arg0=addr，arg1=memory。
}

// 退出
// ---------------------------------------
// 退出[return mem][]是
// Ret[return mem][]是
// RetJmp[return mem][]是的
// Plain[]下一个]
// If[boolean Value][then，else]
// First[]始终，从不]

var genericBlocks = []blockData{
	{name: "Plain"},               // 单个继任者
	{name: "If", controls: 1},     // If控制[0]成功[0]否则成功[1]
	{name: "Defer", controls: 1},  // Succs[0]=延迟排队，成功[1]=延迟恢复。控件[0]是调用op（内存类型）
	{name: "Ret", controls: 1},    // 无后继者，控件[0]值是内存结果
	{name: "RetJmp", controls: 1}, // 无后继者，控件[0]值是尾部调用
	{name: "Exit", controls: 1},   // 无后继者，控件[0]值生成死机

	// 用于删除死代码的瞬态块状态
	{name: "First"}, // 2个后继者，总是拿第一个（第二个死了）
}

func init() {
	archs = append(archs, arch{
		name:    "generic",
		ops:     genericOps,
		blocks:  genericBlocks,
		generic: true,
	})
}
